package me.zhengjie.kwService.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.exception.ParameterException;
import me.zhengjie.kwService.ExamRoomService;
import me.zhengjie.mapper.*;
import me.zhengjie.pojo.dto.ExamRommManualDto;
import me.zhengjie.pojo.dto.ExamRoomDto;
import me.zhengjie.pojo.dto.ExamRoomFindPageTearcherDto;
import me.zhengjie.pojo.dto.ExamRoomRandomDto;
import me.zhengjie.pojo.entity.*;
import me.zhengjie.pojo.vo.ExamRoomVo;
import me.zhengjie.util.AssertUtils;
import me.zhengjie.util.BaseResponse;
import me.zhengjie.utils.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Long
 * @date 2021/6/28 14:04
 */
@Slf4j
@Service
public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomMapper, ExamRoom> implements ExamRoomService {
    @Autowired
    private ExamRoomMapper examRoomMapper;
    @Autowired
    private RoomMapper roomMapper;
    @Autowired
    private OccupyMapper occupyMapper;
    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private ExamTeacherMapper examTeacherMapper;
    @Autowired
    private ExamRoomServiceImpl examRoomService;

    @Override
    public ExamRoomVo findOneById(Integer id) {
        return examRoomMapper.findOneById(id);
    }

    @Override
    public IPage<ExamRoom> findAllExamRoom(Integer pageNo, Integer pageSize) {
        IPage<ExamRoom> page = new Page<>(pageNo, pageSize);
        page = examRoomMapper.selectPage(page, null);
        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse saveExamRoom(ExamRoomDto dto) {
        if (dto != null) {

            Room room = roomMapper.selectById(dto.getRoomId());
            if (room != null && dto.getSeatNumber() > room.getSeatNumber())
                throw new ParameterException("该教室只能容纳" + room.getSeatNumber() + "人");
            if (StringUtils.isNotBlank(dto.getNameCn())) {
                QueryWrapper<ExamRoom> wrapper = new QueryWrapper<>();
                wrapper.eq("name_cn", dto.getNameCn());
                Integer count = examRoomMapper.selectCount(wrapper);
                if (count != 0) throw new ParameterException("考场名已存在,不可重复");
            }


            /*根据教室id查询出对应的教室*/
            AssertUtils.isNotNull(dto.getRoomId(), "教室id不可为空");
            Room one = roomMapper.selectById(dto.getRoomId());
            /*根据教室id和考试的开始时间和结束时间判断该教室是否被占用*/
            QueryWrapper<Occupy> occupyQueryWrapper = new QueryWrapper<>();
            occupyQueryWrapper.eq(one.getId() != null, "type_id", one.getId())
                    .eq("type", 0);
            List<Occupy> list = occupyMapper.selectList(occupyQueryWrapper);
            if (!list.isEmpty()) {
                for (Occupy o : list) {
                    Date endTime = o.getEndTime();
                    Date startTime = o.getStartTime();
                    /*如果传入的考试开始时间在表中的开始时间和结束时间之中则说明考试被占用*/
                    if (dto.getStartTime().getTime() >= startTime.getTime() && dto.getStartTime().getTime() <= endTime.getTime()) {
                        return BaseResponse.failed("该教室在这个时间段已被占用");
                    }
                    if (dto.getEndTime().getTime() >= startTime.getTime() && dto.getEndTime().getTime() <= endTime.getTime()) {
                        return BaseResponse.failed("该教室在这个时间段已被占用");
                    }
                }
            }
            /*如果该教室没被占用，保存教室占用信息到教室、教师占用情况表中*/
            Occupy occupy = new Occupy();
            BeanUtils.copyProperties(dto, occupy);
            occupy.setTypeId(dto.getRoomId());
            occupy.setType("0");
            occupy.setUpdateTime(new Date());
            occupyMapper.insert(occupy);

            /*保存考场信息*/
            ExamRoom examRoom = new ExamRoom();
            BeanUtils.copyProperties(dto, examRoom);
            examRoom.setUpdateTime(new Date());
            save(examRoom);
            return BaseResponse.ok();
        }
        return BaseResponse.failed("传入空值");
    }

    /**
     * 给考场随机分配监考教师
     *
     * @param dto 分配人数、考试id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void random(ExamRoomRandomDto dto) {
        Exam exam = examMapper.selectById(dto.getExamId());
        AssertUtils.isNotNull(exam, "该考试不存在");
        QueryWrapper<Occupy> wrapper = new QueryWrapper<>();
        wrapper.eq("type", "1");
        /*查询出所有监考教师的占用信息*/
        List<Occupy> list = occupyMapper.selectList(wrapper);
        long examStartTime = exam.getStartTime().getTime();
        long examEndTime = exam.getEndTime().getTime();
        List occupyTeachers = getOccupyTeachers(list, examStartTime, examEndTime);

        /*查询出本次考试下的所有考场*/
        QueryWrapper<ExamRoom> examRoomQueryWrapper = new QueryWrapper<>();
        examRoomQueryWrapper.eq("exam_id", exam.getId());
        List<ExamRoom> examRooms = examRoomMapper.selectList(examRoomQueryWrapper);
        AssertUtils.isNotEmpty(examRooms, "没有任何考场可分配监考教师");
        /*循环给每个考场分配指定数量的监考教师*/
        for (ExamRoom examRoom : examRooms) {
            /*查询这个考场是否已经分配了教师*/
            QueryWrapper<ExamTeacher> teacherQueryWrapper = new QueryWrapper<>();
            teacherQueryWrapper.eq("exam_room_id", examRoom.getId())
                    .eq("exam_id", exam.getId());
            Integer count = examTeacherMapper.selectCount(teacherQueryWrapper);
            /*如果分配满了教师则跳过不进行分配,如果分配了但未分配满，则接着继续分配*/
            if (count >= dto.getCount()) continue;
            else if (count > 0 && count <= dto.getCount()) {
                int randomCount = dto.getCount() - count;
                /*查询出所有监考教师的占用信息*/
                /*list = occupyMapper.selectList(wrapper);
                examStartTime = exam.getStartTime().getTime();
                examEndTime = exam.getEndTime().getTime();
                occupyTeachers = getOccupyTeachers(list, examStartTime, examEndTime);*/

                examRoomService.RandomTeacher(exam, occupyTeachers, examRoom, randomCount, occupyMapper, examTeacherMapper);
                continue;
            }
            list = occupyMapper.selectList(wrapper);
            examStartTime = exam.getStartTime().getTime();
            examEndTime = exam.getEndTime().getTime();
            occupyTeachers = getOccupyTeachers(list, examStartTime, examEndTime);
            examRoomService.RandomTeacher(exam, occupyTeachers, examRoom, dto.getCount(), occupyMapper, examTeacherMapper);
        }
    }

    /**
     * 查询出所有本场考试未被分配的监考教师
     *
     * @param dto 考试id
     * @return
     */
    @Override
    public IPage<ExamTeacher> findPage(ExamRoomFindPageTearcherDto dto) {

        Exam exam = examMapper.selectById(dto.getExamId());
        QueryWrapper<Occupy> wrapper = new QueryWrapper<>();
        wrapper.eq("type", "1");
        List<Occupy> list = occupyMapper.selectList(wrapper);
        long examStartTime = exam.getStartTime().getTime();
        long examEndTime = exam.getEndTime().getTime();
        List occupyTeachers = getOccupyTeachers(list, examStartTime, examEndTime);
        Page<ExamTeacher> page = new Page<>(dto.getPage(), dto.getSize());
        QueryWrapper<ExamTeacher> examTeacherQueryWrapper = new QueryWrapper<>();
        examTeacherQueryWrapper.isNull("exam_room_id")
                .notIn(!occupyTeachers.isEmpty(), "teacher_id", occupyTeachers)
                .eq("exam_id", exam.getId());
        page = examTeacherMapper.selectPage(page, examTeacherQueryWrapper);
        return page;
    }

    /**
     * 查询出在这个考试考场的监考教师
     *
     * @param examId     考试id
     * @param examRoomId 考场id
     * @return
     */
    @Override
    public List<ExamTeacher> getExamTeachers(Integer examId, Integer examRoomId) {
        QueryWrapper<ExamTeacher> wrapper = new QueryWrapper<>();
        wrapper.eq("exam_id", examId)
                .eq("exam_room_id", examRoomId);
        return examTeacherMapper.selectList(wrapper);
    }

    /**
     * 手动给考场分配监考教师
     *
     * @param dto 监考教师信息id、考场id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void manual(ExamRommManualDto dto) {
        ExamTeacher examTeacher = examTeacherMapper.selectById(dto.getId());
        UpdateWrapper<ExamTeacher> examTeacherUpdateWrapper = new UpdateWrapper<>();
        examTeacherUpdateWrapper.eq("id", dto.getId())
                .set("exam_room_id", dto.getExamRoomId());
        examTeacherMapper.update(null, examTeacherUpdateWrapper);
        if (dto.getExamRoomId() == null) {
            QueryWrapper<Occupy> wrapper = new QueryWrapper<>();
            wrapper.eq("exam_id", examTeacher.getExamId())
                    .eq("type_id", examTeacher.getTeacherId())
                    .eq("type", "1");
            occupyMapper.delete(wrapper);
        } else {
            Exam exam = examMapper.selectById(examTeacher.getExamId());
            Occupy occupy = new Occupy();
            occupy.setType("1")
                    .setExamId(examTeacher.getExamId())
                    .setTypeId(examTeacher.getTeacherId())
                    .setStartTime(exam.getStartTime())
                    .setEndTime(exam.getEndTime());
            occupyMapper.insert(occupy);
        }


    }

    /**
     * 筛选出在这个时间段被占用的教师
     *
     * @param list          教师占用信息
     * @param examStartTime 考试开始时间
     * @param examEndTime   考试结束时间
     */
    private List getOccupyTeachers(List<Occupy> list, long examStartTime, long examEndTime) {
        LinkedList<Integer> occupyTeachers = new LinkedList<>();
        if (!list.isEmpty()) {
            for (Occupy o : list) {
                long endTime = o.getEndTime().getTime();
                long startTime = o.getStartTime().getTime();
                /*筛选出被占用的教师*/
                if (examStartTime >= startTime && examStartTime <= endTime) {
                    occupyTeachers.add(o.getTypeId());
                }
                if (examEndTime >= startTime && examEndTime <= endTime) {
                    occupyTeachers.add(o.getTypeId());
                }
                if (startTime >= examStartTime && startTime <= examEndTime) {
                    occupyTeachers.add(o.getTypeId());
                }
                if (endTime >= examStartTime && endTime <= examEndTime) {
                    occupyTeachers.add(o.getTypeId());
                }
            }
        }
        return occupyTeachers;
    }

//    /**
//     * 随机给考场分配监考教师
//     *
//     * @param exam         本场考试信息
//     * @param examTeachers 本次考试下所有未分配考场且未被占用的监考教师信息
//     * @param examRoom     要分配的考场
//     * @param randomCount  随机分配的数量
//     */
//    private void RandomTeacher(Exam exam, List<ExamTeacher> examTeachers, ExamRoom examRoom, Integer randomCount, OccupyMapper occupyMapper, ExamTeacherMapper examTeacherMapper) {
//        for (int i = 0; i < randomCount; i++) {
//            if (examTeachers.isEmpty()) break;
//            int index = RandomUtil.randomInt(0, examTeachers.size());
//            ExamTeacher examTeacher = examTeachers.get(index);
//            examTeacher.setExamRoomId(examRoom.getId());
//            examTeachers.remove(index);
//
//            Occupy occupy = new Occupy();
//            occupy.setStartTime(exam.getStartTime())
//                    .setEndTime(exam.getEndTime())
//                    .setType("1")
//                    .setTypeId(examTeacher.getTeacherId())
//                    .setExamId(exam.getId());
//            System.out.println("occupy:" + occupy);
//            occupyMapper.insert(occupy);
//            examTeacherMapper.updateById(examTeacher);
//        }
//    }

    /**
     * 随机给考场分配监考教师
     *
     * @param exam           本场考试信息
     * @param occupyTeachers 被占用得监考老师信息
     * @param examRoom       要分配的考场
     * @param randomCount    随机分配的数量
     */
    private void RandomTeacher(Exam exam, List<ExamTeacher> occupyTeachers, ExamRoom examRoom, Integer randomCount, OccupyMapper occupyMapper, ExamTeacherMapper examTeacherMapper) {

        /*查询出本次考试下所有未分配考场的监考教师信息*/
        QueryWrapper<ExamTeacher> examTeacherQueryWrapper = new QueryWrapper<>();
        examTeacherQueryWrapper.eq("exam_id", exam.getId())
                .notLike("identity", 2)
                .notLike("identity", 1)
                .isNull("exam_room_id")
                .notIn(!occupyTeachers.isEmpty(), "teacher_id", occupyTeachers);
        List<ExamTeacher> examTeachers = examTeacherMapper.selectList(examTeacherQueryWrapper);
        /*筛选出所有女教师*/
        List<ExamTeacher> womanTeachers = examTeachers.stream().filter(examTeacher -> examTeacher.getSex() == 1).collect(Collectors.toList());
        /*筛选出所有男教师*/
        List<ExamTeacher> manTeachers = examTeachers.stream().filter(examTeacher -> examTeacher.getSex() == 0).collect(Collectors.toList());
        int count = 0;
        for (int i = 0; i < randomCount; i++) {
            log.info("考场：{}", examRoom.getNameCn());
            if (womanTeachers != null && !womanTeachers.isEmpty() && manTeachers != null && !manTeachers.isEmpty()) {

                LinkedList<ExamTeacher> list = new LinkedList<>();

                if (count != 2) {
                    count = 2;
                    list.addAll(manTeachers);
                } else {
                    count = 1;
                    list.addAll(womanTeachers);
                }

                if (list != null && !list.isEmpty()) {
                    int index = RandomUtil.randomInt(0, list.size());
                    ExamTeacher teacher = list.get(index);
                    teacher.setExamRoomId(examRoom.getId());

                    log.info("存在男女教师分配的教师：{}  性别：{}", teacher.getNameCn(), teacher.getSex()==1?"女":"男");

                    if (count == 2) manTeachers.remove(teacher);
                    if (count == 1) womanTeachers.remove(teacher);


                    Occupy occupy = new Occupy();
                    occupy.setStartTime(exam.getStartTime())
                            .setEndTime(exam.getEndTime())
                            .setType("1")
                            .setTypeId(teacher.getTeacherId())
                            .setExamId(exam.getId());
                    System.out.println("occupy:" + occupy);
                    occupyMapper.insert(occupy);
                    examTeacherMapper.updateById(teacher);
                }
            } else {


                if (examTeachers.isEmpty()) break;
                int index = RandomUtil.randomInt(0, examTeachers.size());
                ExamTeacher examTeacher = examTeachers.get(index);
                examTeacher.setExamRoomId(examRoom.getId());
                examTeachers.remove(index);

                log.info("男女教师不匹配的教师：{}  性别：{}", examTeacher.getNameCn(),  examTeacher.getSex()==1?"女":"男");

                Occupy occupy = new Occupy();
                occupy.setStartTime(exam.getStartTime())
                        .setEndTime(exam.getEndTime())
                        .setType("1")
                        .setTypeId(examTeacher.getTeacherId())
                        .setExamId(exam.getId());
                System.out.println("occupy:" + occupy);
                occupyMapper.insert(occupy);
                examTeacherMapper.updateById(examTeacher);
            }
        }
    }

}

